/*
 * 	This file is a modification of phylip's drawtree.c. It is also part 
 *  of MVD_GUI.
 *
 *  MVD_GUI is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  MVD_GUI is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with MVD_GUI.  If not, see <http://www.gnu.org/licenses/>.
 *  This version copyright 2010 Desmond Schmidt
 */
#include <stdio.h>
#include <syslog.h>
#include <math.h>
#include <string.h>
#include "phylip.h"
#include "draw.h"

#ifdef max
#undef max
#endif
#define max(a,b) ((a > b) ? a : b)
#ifdef min
#undef min
#endif
#define min(a,b) ((a > b) ? b : a)
#define max4(a,b,c,d) (max(max(a,b),max(c,d)))
#define min4(a,b,c,d) (min(min(a,b),min(c,d)))

extern FILE *plotfile;
double treeline;
double labelline;
double linewidth;
double oldx;
double oldy;
double clipx1;
double clipx0;
double clipy1;
double clipy0;
int pagecount;
pentype lastpen;
extern enum {yes,no} penchange,oldpenchange;
extern double ycorner;
extern double xsize;
extern double ysize;
extern double xmargin;
extern double xcorner;
extern double pagex;
extern double pagey;
extern double hpmargin;
extern double vpmargin;
extern double ymargin;
extern double paperx;
extern double papery;
extern double xunitspercm;
extern double yunitspercm;
extern double labelheight;
extern double yscale;
extern double expand;
extern plottertype plotter;
extern char fontname[FONT_NAME_LEN];
extern FILE *plotfile;
static short helvetica_metric[] = { 718,
278,278,355,556,556,889,667,222,333,333,389,584,278,333,278,278,556,556,556,
556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667,667,722,722,667,
611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944,667,
667,611,278,278,278,469,556,222,556,556,500,556,556,278,556,556,222,222,500,
222,833,556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,333,556,
556,167,556,556,556,556,191,333,556,333,333,500,500,0,556,556,556,278,0,537,
350,222,333,333,556,1000,1000,0,611,0,333,333,333,333,333,333,333,333,0,333,
333,0,333,333,333,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,370,0,0,0,0,556,
778,1000,365,0,0,0,0,0,889,0,0,0,278,0,0,222,611,944,611,0,0,0};
static short helveticabold_metric[] = {718, /* height */ 
278,333,474,556,556,889,722,278,333,333,389,584,278,333,278,278,556,556,556,
556,556,556,556,556,556,556,333,333,584,584,584,611,975,722,722,722,722,667,
611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944,667,
667,611,333,278,333,584,556,278,556,611,556,611,556,333,611,611,278,278,556,
278,889,611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,333,556,
556,167,556,556,556,556,238,500,556,333,333,611,611,0,556,556,556,278,0,556,
350,278,500,500,556,1000,1000,0,611,0,333,333,333,333,333,333,333,333,0,333,
333,0,333,333,333,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,370,0,0,0,0,611,
778,1000,365,0,0,0,0,0,889,0,0,0,278,0,0,278,611,944,611,0,0,0};
static short timesroman_metric[] = {662,
250,333,408,500,500,833,778,333,333,333,500,564,250,333,250,278,500,500,500,
500,500,500,500,500,500,500,278,278,564,564,564,444,921,722,667,667,722,611,
556,722,722,333,389,722,611,889,722,722,556,722,667,556,611,722,722,944,722,
722,611,333,278,333,469,500,333,444,500,444,500,444,333,500,500,278,278,500,
278,778,500,500,500,500,333,389,278,500,500,722,500,500,444,480,200,480,541,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,333,500,
500,167,500,500,500,500,180,444,500,333,333,556,556,0,500,500,500,250,0,453,
350,333,444,444,500,1000,1000,0,444,0,333,333,333,333,333,333,333,333,0,333,
333,0,333,333,333,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,889,0,276,0,0,0,0,611,
722,889,310,0,0,0,0,0,667,0,0,0,278,0,0,278,500,722,500,0,0,0};
static short timesitalic_metric[] = {660, /* height */ 
250,333,420,500,500,833,778,333,333,333,500,675,250,333,250,278,500,500,500,
500,500,500,500,500,500,500,333,333,675,675,675,500,920,611,611,667,722,611,
611,722,722,333,444,667,556,833,667,722,611,722,611,500,556,722,611,833,611,
556,556,389,278,389,422,500,333,500,500,444,500,444,278,500,500,278,278,444,
278,722,500,500,500,500,389,389,278,500,444,667,444,444,389,400,275,400,541,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,389,500,
500,167,500,500,500,500,214,556,500,333,333,500,500,0,500,500,500,250,0,523,
350,333,556,556,500,889,1000,0,500,0,333,333,333,333,333,333,333,333,0,333,
333,0,333,333,333,889,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,889,0,276,0,0,0,0,556,
722,944,310,0,0,0,0,0,667,0,0,0,278,0,0,278,500,667,500,0,0,0};
static short timesbold_metric[] = {681, /* height */ 
250,333,555,500,500,1000,833,333,333,333,500,570,250,333,250,278,500,500,500,
500,500,500,500,500,500,500,333,333,570,570,570,500,930,722,667,722,722,667,
611,778,778,389,500,778,667,944,722,778,611,778,722,556,667,722,722,1000,722,
722,667,333,278,333,581,500,333,500,556,444,556,444,333,500,556,278,333,556,
278,833,556,500,556,556,444,389,333,556,500,722,500,500,444,394,220,394,520,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,333,500,500,
167,500,500,500,500,278,500,500,333,333,556,556,0,500,500,500,250,0,540,350,
333,500,500,500,1000,1000,0,500,0,333,333,333,333,333,333,333,333,0,333,333,
0,333,333,333,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,300,0,0,0,0,667,778,
1000,330,0,0,0,0,0,722,0,0,0,278,0,0,278,500,722,556,0,0,0};
static short timesbolditalic_metric[] = {662, /* height */ 
250,389,555,500,500,833,778,333,333,333,500,570,250,333,250,278,500,500,500,
500,500,500,500,500,500,500,333,333,570,570,570,500,832,667,667,667,722,667,
667,722,778,389,500,667,611,889,722,722,611,722,667,556,611,722,667,889,667,
611,611,333,278,333,570,500,333,500,500,444,500,444,333,500,556,278,278,500,
278,778,556,500,500,500,389,389,278,556,444,667,500,444,389,348,220,348,570,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,389,500,
500,167,500,500,500,500,278,500,500,333,333,556,556,0,500,500,500,250,0,500,
350,333,500,500,500,1000,1000,0,500,0,333,333,333,333,333,333,333,333,0,333,
333,0,333,333,333,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,944,0,266,0,0,0,0,611
,722,944,300,0,0,0,0,0,722,0,0,0,278,0,0,278,500,722,500,0,0,0};
/* added for vrml - danieyek 981111 */
/* Returned angle in radian */
/* A related function is "double angleBetVectors(Xu, Yu, Xv, Yv)"
   in drawtree.c */
double computeAngle(double oldx, double oldy, double newx, double newy)
{
  	double angle;

  	if ((newx-oldx) == 0 )
  	{
    	/* pi/2 or -pi/2! */
    	if (newy > oldy) 
			angle = pi/2;
    	else if (newy < oldy) 
			angle = -pi/2;
    	else 
    	{
      		/* added - danieyek 990130 */
      		/* newx = oldx; newy = oldy; one point on top of the other! 
         	If new and old correspond to 2 points, changes are that the 2 coordinates
         	are not identical under double precision value. */
      		syslog(LOG_ERR, 
      		"Angle can't be computed, 2 points on top of each other in computeAngle()!\n");
      		angle = 0;
    	}
  	}
  	else
  	{
    	angle = atan( (newy-oldy)/(newx-oldx) );

    	if (newy >= oldy && newx >= oldx)
    	{
      		/* First quardrant - no adjustment */
    	}
    	else if (newx <= oldx)
    	{
      		/* Second (angle = negative) and
         	third (angle = positive) quardrant */
      		angle = pi + angle;
    	}
    	else if (newy <= oldy && newx >= oldx)
    	{
      		/* Fourth quardrant; "angle" is negative! */
      		angle = 2*pi + angle;
    	}
    	else
    	{
      		/* Should never get here. */
      		syslog(LOG_ERR, "Programming error in computeAngle()!\n");
    	}
  	}
  	return angle;
}
/** measure height of text in current font */
double heighttext(char *fontname)
{
	short afmetric[256];
    metricforfont(fontname,afmetric);  
    return (double)afmetric[0];
}
/* set up initial characteristics of plotter or printer */
void plotrparms(int ntips)
{
	double xsizehold, ysizehold;

	xsizehold = xsize;
	ysizehold = ysize;
	penchange = no;
	xcorner = 0.0;
	ycorner = 0.0;
  	switch (plotter)
	{
		case lw:
    		penchange = yes;
    		xunitspercm = 28.346456693;
    		yunitspercm = 28.346456693;
    		xsize = pagex;
    		ysize = pagey;
    		break;
  		default:
    		break;
	}
	if (xsizehold != 0.0 && ysizehold != 0.0) 
	{
    	xmargin = xmargin * xsize / xsizehold;
    	ymargin = ymargin * ysize / ysizehold;
  	}
} 
void finishplotter()
{
	switch (plotter) 
	{
  		case lw:
			fprintf(plotfile, "stroke showpage \n\n");
			fprintf(plotfile,"%%%%PageTrailer\n");
			fprintf(plotfile,"%%%%PageFonts: %s\n",fontname);
			fprintf(plotfile,"%%%%Trailer\n");
			fprintf(plotfile,"%%%%DocumentFonts: %s\n",fontname);
    		break;
  		default:
   			break;
	}
}
void plotpb()
{
	pagecount++;
	fprintf(plotfile,"\n showpage \n%%%%PageTrailer\n");
	fprintf(plotfile,"%%%%DocumentFonts: %s\n",fontname);
	fprintf(plotfile,"%%%%\n%%%%Page: %d %d\n",pagecount,pagecount);
	fprintf(plotfile,"%%%%PageBoundingBox: 0 0 %d %d\n",
		(int)(xunitspercm*paperx),(int)(yunitspercm*papery));
  	fprintf(plotfile,"%%%%PageFonts: (atend)\n%%%%BeginPageSetup\n%%%%PaperSize: A4\n");
  	fprintf(plotfile,"0 0 moveto\n"); /* hack to make changepen work w/o errors */
  	changepen(lastpen);
}

void metricforfont( char *fontname, short *fontmetric )
{
	int i;  
  	if ((strcmp(fontname,"Helvetica") == 0) ||
      	(strcmp(fontname,"Helvetica-Oblique") == 0))
		for (i=31;i<256;++i)
		  	fontmetric[i-31] = helvetica_metric[i-31];
  	else if ((strcmp(fontname,"Helvetica-Bold") == 0) ||
      	(strcmp(fontname,"Helvetica-BoldOblique") == 0))
		for (i=31;i<256;++i)
		  	fontmetric[i-31] = helveticabold_metric[i-31];
  	else if (strcmp(fontname,"Times-Roman") == 0)
    	for (i=31;i<256;++i)
      		fontmetric[i-31] = timesroman_metric[i-31];
  	else if (strcmp(fontname,"Times") == 0)
    	for (i=31;i<256;++i)
      		fontmetric[i-31] = timesroman_metric[i-31];
  	else if (strcmp(fontname,"Times-Italic") == 0)
    	for (i=31;i<256;++i)
      		fontmetric[i-31] = timesitalic_metric[i-31];
  	else if (strcmp(fontname,"Times-Bold") == 0)
    	for (i=31;i<256;++i)
      		fontmetric[i-31] = timesbold_metric[i-31];
  	else if (strcmp(fontname,"Times-BoldItalic") == 0)
    	for (i=31;i<256;++i)
      		fontmetric[i-31] = timesbolditalic_metric[i-31];
  	else if (strncmp(fontname,"Courier",7) == 0)
	{
    	fontmetric[0] = 562;  
    	for (i=32;i<256;++i)
      		fontmetric[i-31] = (short)600;
  	}
	else
		syslog(LOG_ERR,"Unknown font %s\n",fontname );
}
double lengthtext( char *pstring, int nchars, char *fontname )
{
	int i;
	static double sumlength;
	int sumbigunits;
	short afmetric[256];

	sumlength = 0.0;
	metricforfont( fontname, afmetric );
	sumbigunits = 0;
	for (i = 0; i < nchars; i++) 
		sumbigunits += afmetric[(int)(1+(unsigned char)pstring[i] - 32)];
	sumlength = (double)sumbigunits;
	return sumlength;
}
int pointinrect(double x,double y,double x0,double y0,double x1,double y1)
{
	double tmp;
	if (x0 > x1)
    	tmp = x0,
	x0  = x1,
	x1  = tmp;
	if (y0 > y1)
		tmp = y0,
	y0  = y1,
	y1  = tmp;
  	return ((x >= x0 && x <= x1) && (y >= y0 && y <= y1));
}
/**
 * Check if any of the corners of either square are contained within the 
 * other one. This catches MOST cases, the last one (two) is two thin   
 * bands crossing each other (like a '+' )                                
 */
int rectintersects( double xmin1,double ymin1,double xmax1,double ymax1,
	double xmin2,double ymin2,double xmax2,double ymax2 )
{
  	double temp;
  
  	if (xmin1 > xmax1)
    	temp  = xmin1;  xmin1 = xmax1;  xmax1 = temp;
  	if (xmin2 > xmax2)
    	temp  = xmin2;  xmin2 = xmax2;  xmax2 = temp;
  	if (ymin1 > ymax1)
    	temp  = ymin1;  ymin1 = ymax1;  ymax1 = temp;
  	if (ymin2 > ymax2)
    	temp  = ymin2;  ymin2 = ymax2;  ymax2 = temp;
  
  return (pointinrect(xmin1,ymin1,xmin2,ymin2,xmax2,ymax2) ||
          pointinrect(xmax1,ymin1,xmin2,ymin2,xmax2,ymax2) ||
          pointinrect(xmin1,ymax1,xmin2,ymin2,xmax2,ymax2) ||
          pointinrect(xmax1,ymax1,xmin2,ymin2,xmax2,ymax2) ||
          pointinrect(xmin2,ymin2,xmin1,ymin1,xmax1,ymax1) ||
          pointinrect(xmax2,ymin2,xmin1,ymin1,xmax1,ymax1) ||
          pointinrect(xmin2,ymax2,xmin1,ymin1,xmax1,ymax1) ||
          pointinrect(xmax2,ymax2,xmin1,ymin1,xmax1,ymax1) || 
          (xmin1 >= xmin2 && xmax1 <= xmax2 &&
           ymin2 >= ymin1 && ymax2 <= ymax1) ||
          (xmin2 >= xmin1 && xmax2 <= xmax1 &&
           ymin1 >= ymin2 && ymax1 <= ymax2)); 
}
void plottext( char *pstring, int nchars, double height_, double cmpress2,
	double x, double y, double slope, char *fontname )
{
	struct LOC_plottext text;
	double pointsize;
	double textlen;
	double px0,py0,px1,py1; /* square bounding box of text */
  
  	pointsize = (((height_ / xunitspercm) / 2.54) * 72.0);
  
	text.height = height_;
	text.compress = cmpress2;
	text.xx = x;
	text.yy = y;
	text.sinslope = sin(pi * slope / 180.0);
	text.cosslope = cos(pi * slope / 180.0);

 	if (plotter == lw)  
	{
		/* If there's NO possibility that the line intersects the square bounding
		 * box of the font, leave it out. Otherwise, let postscript clip to region.
		 * Compute text boundary, be REAL generous. */
		textlen = (lengthtext(pstring,nchars,fontname)/1000)*pointsize;
		px0 = min4(x + (text.cosslope * pointsize),
		           x - (text.cosslope * pointsize),
		           x + (text.cosslope * pointsize) + (text.sinslope * textlen),
		           x - (text.cosslope * pointsize) + (text.sinslope * textlen))
		  /28.346;
		px1 = max4(x + (text.cosslope * pointsize),
		           x - (text.cosslope * pointsize),
		           x + (text.cosslope * pointsize) + (text.sinslope * textlen),
		           x - (text.cosslope * pointsize) + (text.sinslope * textlen))
		  /28.346;
		py0 = min4(y + (text.sinslope * pointsize),
		           y - (text.sinslope * pointsize),
		           y + (text.sinslope * pointsize) + (text.cosslope * textlen),
		           y - (text.sinslope * pointsize) + (text.cosslope * textlen))
		  /28.346;
		py1 = max4(y + (text.sinslope * pointsize),
		           y - (text.sinslope * pointsize),
		           y + (text.sinslope * pointsize) + (text.cosslope * textlen),
		           y - (text.sinslope * pointsize) + (text.cosslope * textlen))
		  /28.346;

		/* if rectangles intersect, print it. */
		if (rectintersects(px0,py0,px1,py1,clipx0,clipy0,clipx1,clipy1)) 
		{
			fprintf(plotfile,"gsave\n");
			fprintf(plotfile,"/%s findfont %f scalefont setfont\n",fontname,
				pointsize);
			fprintf(plotfile,"%f %f translate %f rotate\n",
				x-(clipx0*xunitspercm),y-(clipy0*xunitspercm),-slope);
			fprintf(plotfile,"0 0 moveto\n");
			fprintf(plotfile,"(%s) show\n",pstring);
			fprintf(plotfile,"grestore\n");
		}
	}
}
void changepen( pentype pen )
{
	lastpen = pen;
 	switch (pen) 
	{
		case treepen:
			linewidth = treeline;
			if ( plotter == lw ) 
			{
				fprintf(plotfile, "stroke %8.2f setlinewidth \n", 
					treeline);
			  	fprintf(plotfile, " 1 setlinecap 1 setlinejoin \n");
			}
			break;
		case labelpen:
			linewidth = labelline;
			if ( plotter == lw ) 
			{
			  	fprintf(plotfile, " stroke%8.2f setlinewidth \n", 
					labelline);
			  	fprintf(plotfile, "1 setlinecap 1 setlinejoin \n");
			}
			break;
  	}
} 
void plot( pensttstype pen, double xabs, double yabs )
{
	switch (plotter) 
	{
		case lw:
  			if (pen == pendown)
			{
			/* If there's NO possibility that the line interesects the page, 
			 * leave it out. Otherwise, let postscript clip it to the page. */
      			if (!((xabs > clipx1*xunitspercm &&  oldx > clipx1*xunitspercm) ||
          			(xabs < clipx0*xunitspercm &&  oldx < clipx0*xunitspercm) ||
          			(yabs > clipy1*yunitspercm &&  oldy > clipy1*yunitspercm) ||
          			(yabs < clipy0*yunitspercm &&  oldy < clipy0*yunitspercm)))
        		fprintf(plotfile, "%8.2f %8.2f %8.2f %8.2f l\n",
                oldx-(clipx0*xunitspercm), oldy-(clipy0*yunitspercm),
                xabs-(clipx0*xunitspercm), yabs-(clipy0*yunitspercm));
    		}
    		oldx = xabs;
    		oldy = yabs;
  			break;
	}
} 
/**
 * Simplified plotter routine. Since most plotters in the original 
 * phylip no longer work we just use postscript. We can then convert 
 * the output into any format using ghostscript.
 */
void initplotter()
{
 	treeline = 0.18 * labelheight * yscale * expand;
  	labelline = 0.06 * labelheight * yscale * expand;
  	linewidth = treeline;
 	switch (plotter) 
	{
  		case lw:     /* write conforming postscript */
			fprintf(plotfile,"%%!PS-Adobe-2.0\n");
			fprintf(plotfile,"%%%%Title: Phylip Tree Output\n");
			fprintf(plotfile,"%%%%DocumentFonts: (atend)\n");
			fprintf(plotfile,"%%%%Pages: %d 1\n",
				   ((int)((pagex-hpmargin-0.01)/(paperx-hpmargin))+1)*
				       ((int)((pagey-vpmargin-0.01)/papery-vpmargin)+1));
			fprintf(plotfile,"%%%%BoundingBox: 0 0 612 792\n");
			fprintf(plotfile,"%%%%DocumentPaperSizes: A4\n"); 
			fprintf(plotfile,"%%%%Orientation: Portrait\n"); 
			fprintf(plotfile,"%%%%EndComments\n");
			fprintf(plotfile,"/l {newpath moveto lineto stroke} def\n");
			fprintf(plotfile,"%%%%EndProlog\n%%%%\n");
			fprintf(plotfile,"%%%%Page: 1 1\n");
			fprintf(plotfile,"%%%%PageBoundingBox: 0 0 %d %d\n",
				    (int)(xunitspercm*paperx),(int)(yunitspercm*papery));
			fprintf(plotfile,"%%%%PageFonts: (atend)\n%%%%BeginPageSetup\n");
			fprintf(plotfile,"%%%%PaperSize: A4\n");
			fprintf(plotfile," 1 setlinecap \n 1 setlinejoin  \n");
			fprintf(plotfile, "%8.2f setlinewidth newpath \n", treeline);
			break;
		default:
    		break;
  	}
}
/**
 * Draw a tree
 * @param fontname name of postscript font
 * @param xoffset to be filled in with tree-position
 * @param yoffset to be filled in with tree-position
 * @param root the root of the tree to draw
 */
void drawit( char *fontname, double *xoffset, double *yoffset, node *root )
{
	int i, j, xpag, ypag;

  	(*xoffset) = 0.0;
  	(*yoffset) = 0.0;
  	xpag = (int)((pagex-hpmargin-0.01)/(paperx - hpmargin))+1;
  	ypag = (int)((pagey-vpmargin-0.01)/(papery - vpmargin))+1;
  	pagecount = 1;
	for ( j=0; j<ypag; ++j )
	{
  		for (i=0; i<xpag; ++i)
		{
			clipx0 = (double)i*(paperx -  hpmargin);
			clipx1 = (double)(i*(paperx -  hpmargin))+(paperx - hpmargin);
			clipy0 = (double)(j*(papery -  vpmargin));
			clipy1 = (double)(j*(papery-hpmargin))+(papery+vpmargin);
			plottree(root, root);
			plotlabels( fontname );
			if (!(i == xpag - 1 && j == ypag - 1) && plotter == lw)
				plotpb(); /* page break */
		}
	}
}

